package com.nenglai.common.crud;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nenglai.common.util.HelperUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Search Util
 *
 * @author kongdehui
 * @date 2021/04/21 20:05
 **/
public class SearchUtil {

    private Map<String, Operator> map = new HashMap<>();

    {
        EQ eq = new EQ();
        map.put("=", eq);
        map.put("EQ", eq);
        NE ne = new NE();
        map.put("!=", ne);
        map.put("<>", ne);
        map.put("NE", ne);
        LT lt = new LT();
        map.put("<", lt);
        map.put("LT", lt);
        LE le = new LE();
        map.put("<=", le);
        map.put("LE", le);
        GT gt = new GT();
        map.put(">", gt);
        map.put("GT", gt);
        GE ge = new GE();
        map.put(">=", ge);
        map.put("GE", ge);
        map.put("BETWEEN", new BETWEEN());
        map.put("NOTBETWEEN", new NOTBETWEEN());
        map.put("LIKE", new LIKE());
        map.put("NOTLIKE", new NOTLIKE());
        map.put("LIKELEFT", new LIKELEFT());
        map.put("LIKERIGHT", new LIKERIGHT());
        map.put("ISNULL", new ISNULL());
        map.put("ISNOTNULL", new ISNOTNULL());
        map.put("IN", new IN());
        map.put("NOTIN", new NOTIN());
    }

    public void addOperator(String key, Operator operator) {
        map.put(key.toUpperCase(), operator);
    }

    /**
     * 查询
     *
     * @param query
     * @param next
     * @param parallel
     * @return void
     * @author kongdehui
     * @date 2021/04/21 20:17
     */
    public boolean condition(QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
        /**
         * 获取属性
         * {"property": "属性", "key": "键值", "field": "字段值"}
         * 存在属性值以属性值为准，否则将key值计算驼峰设置为属性值
         */
        String property = (String) next.getOrDefault(
                "property",
                HelperUtil.lineToHump(next.getString("key"))
        );
        if (StringUtils.isEmpty(property)) {
            return false;
        }

        /**
         * 通过属性值获取字段类型
         */
        Class<?> clazz = parallel.get(property);
        Object value = next.getObject("value", clazz); // 获取值
        if (Objects.isNull(value)) {
            return false;
        }

        boolean filterEmpty = (boolean) next.getOrDefault("filterEmpty", true);
        if (filterEmpty && ObjectUtils.isEmpty(value)) {
            return false;
        }

        /**
         * 获取字段值
         * {"property": "属性", "key": "键值", "field": "字段值"}
         * 存在Field值以field值为准，否则将key值计算下划线设置为字段值
         */
        String field = (String) next.getOrDefault(
                "field",
                HelperUtil.humpToLine(next.getString("key"))
        );

        String type = (String) next.getOrDefault("type", "eq");
        Operator operator = map.get(type.toUpperCase());
        if (operator != null) {
            return operator.execute(field, value, property, query, next, parallel);
        }

        return false;
    }

    /**
     * 排序
     *
     * @param query
     * @param next
     * @return void
     * @author kongdehui
     * @date 2021/04/21 20:22
     */
    public void sort(QueryWrapper<?> query, JSONObject next) {
        String field = (String) next.getOrDefault(
                "field",
                HelperUtil.humpToLine(next.getString("key"))
        );
        if (StringUtils.isEmpty(field)) {
            return;
        }

        String cmd = next.getOrDefault("value", "ASC").toString().toUpperCase();
        if ("ASC".equals(cmd)) {
            query.orderByAsc(field);
        }

        if ("DESC".equals(cmd)) {
            query.orderByDesc(field);
        }
    }

    public interface Operator {
        boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel);
    }

    private static class EQ implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.eq(field, value);
            return true;
        }
    }

    private static class NE implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.ne(field, value);
            return true;
        }
    }

    private static class LT implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.lt(field, value);
            return true;
        }
    }

    private static class LE implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.le(field, value);
            return true;
        }
    }

    private static class GT implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.gt(field, value);
            return true;
        }
    }

    private static class GE implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.ge(field, value);
            return true;
        }
    }

    private static class BETWEEN implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            Class<?> clazz = parallel.get(property);
            Object value2 = next.getObject("value2", clazz); // 获取值
            if (Objects.isNull(value2)) {
                return false;
            }
            query.between(field, value, value2);
            return true;
        }
    }

    private static class NOTBETWEEN implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            Class<?> clazz = parallel.get(property);
            Object value2 = next.getObject("value2", clazz); // 获取值
            if (Objects.isNull(value2)) {
                return false;
            }
            query.notBetween(field, value, value2);
            return true;
        }
    }

    private static class LIKE implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.like(field, value);
            return true;
        }
    }

    private static class NOTLIKE implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.notLike(field, value);
            return true;
        }
    }


    private static class LIKELEFT implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.likeLeft(field, value);
            return true;
        }
    }

    private static class LIKERIGHT implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.likeRight(field, value);
            return true;
        }
    }

    private static class ISNULL implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.isNull(field);
            return true;
        }
    }

    private static class ISNOTNULL implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.isNotNull(field);
            return true;
        }
    }

    private static class IN implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.in(field, value);
            return true;
        }
    }

    private static class NOTIN implements Operator {

        @Override
        public boolean execute(String field, Object value, String property, QueryWrapper<?> query, JSONObject next, Map<String, Class<?>> parallel) {
            query.notIn(field, value);
            return true;
        }
    }

}
